#include"../inc/HttpConnection.h"
#include<string>
#include<iostream>
#include<string.h>

#include<unistd.h>
#include<fcntl.h>
#include<dirent.h>
#include<sys/stat.h>

using std::string;

namespace http_server
{
    typedef HttpConnection HT;
    HT::HttpConnection(int fd, EpollLoop* loop)
    : _sio(fd), _sock(fd), _serv(getLocal(fd)), _clie(getPeer(fd)), _event(loop), _shutdownflag(false)
    {}

    HT::~HttpConnection()
    {
        disconnect();
    }

    void HT::checkRequest()
    {
        char request[1024];
        _sio.read_line(request, sizeof(request));
        struct stat sbuf;

	    char method[16], path[256], protocol[16];
	    // 以空格为标志 通过正则表达式匹配字符串
	    sscanf(request, "%[^ ] %[^ ] %[^ ]", method, path, protocol);

        if(strncasecmp("GET", method, 3) != 0)
        {
            perror("request err");
            return;
        }

	    string file = path+1;
        // 默认无输入访问为目录
        if(strcmp(path, "/") == 0)
        {
            file = "./";
        }
        
        int ret = stat(file.c_str(), &sbuf);
        if(-1 == ret)
        {
            sendErr(404, "NOT FOUND", "No such file or direntry");
            return;
        }

        // 判断文件类型 并根据类型作出对应应答
        if(S_ISREG(sbuf.st_mode))		// 普通文件
        {
            printf("---Its a File.\n");
            sendRespond(200, "OK", getFileType(file), sbuf.st_size);
            sendFile(file.c_str());
        }
        else if(S_ISDIR(sbuf.st_mode))
        {
            printf("---Its a Dir.\n");
            sendRespond(200, "OK", getFileType(".html"), -1);
            sendDir(file.c_str());
        }
    }

    void HT::sendFile(const char* file)
    {
        int n = 0, ret;
        char buf[1024];

        int fd = open(file, O_RDONLY);
        if(fd == -1)
        {
            sendErr(404, "NOT FOUND", "No such file or direntry");
            exit(1);
        }

        while((n = read(_sock.getFd(), buf, sizeof(buf))) > 0)
        {
            ret = send(_sock.getFd(), buf, n, 0);
            if(ret == -1)
            {
                if(errno == EAGAIN)continue;
                else if(errno == EINTR)continue;	
                else 
                {
                    perror("send err");
                    exit(1);
                }
            }
        }
    }

    void HT::sendDir(const char* dirname)
    {
        int i, ret;

        // 拼接一个html页面
        char buf[4096] = {0};

        sprintf(buf, "<html><head><title>目录名: %s</title></head>", dirname);
        sprintf(buf+strlen(buf), "<body><h1>当前目录: %s</h1><table>", dirname);

        char enstr[1024] = {0};
        char path[1024] = {0};

        struct dirent** ptr;
        int num = scandir(dirname, &ptr, NULL, alphasort);

        // 递归遍历目录
        for(i = 0; i < num; i++)
        {
            char* name = ptr[i]->d_name;

            sprintf(path, "%s/%s", dirname, name);
            printf("path = %s ===================\n", path);

            struct stat st;
            stat(path, &st);

            _sio.encode(enstr, sizeof(enstr), name);
        
            if(S_ISREG(st.st_mode))		
                sprintf(buf + strlen(buf), 
                        "<tr><td><a href=\"%s\">%s</a></td><td>%ld</td></tr>",
                        enstr, name, (long)st.st_size);
            else if(S_ISDIR(st.st_mode))
                sprintf(buf + strlen(buf),
                        "<tr><td><a herf=\"%s/\">%s/</a></td><td>%ld</td></tr>",
                        enstr, name, (long)st.st_size);

            ret = send(_sock.getFd(), buf, strlen(buf), 0);
            if(-1 == ret)
            {
                if(errno == EAGAIN)
                {
                    perror("send err:");
                    continue;
                }
                else if(errno == EINTR)
                {
                    perror("send err:");
                    continue;
                }
                else 
                {
                    perror("send err:");
                    exit(1);
                }
            }

            memset(buf, 0, sizeof(buf));
        }

        sprintf(buf+strlen(buf), "</table></body></html>");
        send(_sock.getFd(), buf, strlen(buf), 0);

        printf("dir message sent!\n");
    }

    void HT::sendErr(int status, string title, string text)
    {
        char buf[4096] = {0};

        sprintf(buf, "%s %d %s\r\n", "HTTP/1.1", status, title.c_str());
        sprintf(buf+strlen(buf), "Content-Type:%s\r\n", "text/gtml");
        sprintf(buf+strlen(buf), "Content-Length:%d\r\n", -1);
        sprintf(buf+strlen(buf), "Connection: close\r\n");
        send(_sock.getFd(), buf, strlen(buf), 0);
        send(_sock.getFd(), "\r\n", 2, 0);

        memset(buf, 0, sizeof(buf));

        sprintf(buf, "<html><head><title>%d %s</title></head>\n", status, title.c_str());
        sprintf(buf+strlen(buf), "<body bgcolor=\"#cc99cc\"><h2 align=\"center\">%d %s</h2>\n", status, text.c_str());
        sprintf(buf+strlen(buf), "%s\n", text.c_str());
        sprintf(buf+strlen(buf), "<hr>\n</body>\n</html>\n");
        send(_sock.getFd(), buf, strlen(buf), 0);
    }


    string HT::getFileType(string file)
    {
        const char* ret = strrchr(file.c_str(), '.');
        if(ret == NULL)
            return "text/plain; charset=utf-8";
        if(strcmp(ret, ".html") == 0 || strcmp(ret, ".htm") == 0)
            return "text/html; charset=utf-8";
        if(strcmp(ret, ".jpeg") == 0 || strcmp(ret, ".jpg") == 0)
            return "image/jepg";
        if(strcmp(ret, ".gif") == 0)
            return "image/gif";
        if(strcmp(ret, ".png") == 0)
            return "image/png";
        if(strcmp(ret, ".css") == 0)
            return "text/css";
        if(strcmp(ret, ".au") == 0)
            return "audio/basic";
        if(strcmp(ret, ".wav") == 0)
            return "audio/wav";
        if(strcmp(ret, ".mp3") == 0)
            return "audio/mpeg";
        if(strcmp(ret, ".midi") == 0)
            return "audio/midi";
        if(strcmp(ret, ".avi") == 0)
            return "video/x-msvideo";
        if(strcmp(ret, ".mov") == 0)
            return "video/quicktime";
        
        return "text/plain; charset=utf-8";
    }

    void HT::sendRespond(int err_no, string disp, string type, int len)
    {
        char buf[1024];
        sprintf(buf, "HTTP/1.1 %d %s\r\n", err_no, disp.c_str());
        sprintf(buf+strlen(buf), "Content-Type:%s\r\n", type.c_str());
        sprintf(buf+strlen(buf), "Content-Length:%d\r\n", len);
        send(_sock.getFd(), buf, strlen(buf), 0);
        send(_sock.getFd(), "\r\n", 2, 0);
    }

    Address HT::getLocal(int fd)
    {
        sockaddr_in localaddr;
        socklen_t len = sizeof(localaddr);
        memset(&localaddr, 0, (size_t)len);
        int ret = getsockname(fd, (sockaddr*)&localaddr, &len);
        if(ret)
            perror("getsockname err");
        
        return Address(localaddr);
    }
    
    Address HT::getPeer(int fd)
    {
        sockaddr_in peeraddr;
        socklen_t len = sizeof(peeraddr);
        memset(&peeraddr, 0, (size_t)len);
        int ret = getpeername(fd, (sockaddr*)&peeraddr, &len);
        if(ret)
            perror("getpeername err");
        
        return Address(peeraddr);
    }

    void HT::disconnect()
    {
        printf("Connection Closed.\n");
        _event->delEvent(_sock.getFd());
        if(!_shutdownflag)
        {
            _sock.Shutdown();
            _shutdownflag = true;
        }
    }
}